/*STM32芯片驱动头文件*/
#include "usart.h" 

/*自己的驱动头文件*/
#include "driver_wireless.h"
#include "serial.h"
#include "delay.h"

/*开源的驱动头文件*/
#include "cJson.h"

/*C语言头文件*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>


/**********************************			修改这些			****************************************/
#define WIFI_SSID "XGZX-HUAWEI"								// WIFI用户名
#define WIFI_PASSWORD "xgzx2016"							// WIFI密码
#define ONENET_MQTT_PRODUCT_ID "xUHsdh4wh3"					// OneNET MQTT产品ID
#define ONENET_MQTT_DEVICE_NAME "test"						// OneNET MQTT设备名称
// token
#define ONENET_MQTT_TOKEN "version=2018-10-31&res=products%2FxUHsdh4wh3%2Fdevices%2Ftest&et=2058528898&method=md5&sign=yj4TmkDq7AGJcRp%2FZNZLFw%3D%3D"
/**********************************			修改这些			****************************************/

/**********************************			无需修改			****************************************/
//AT指令
#define WIRELESS_WIFI_INFO "AT+CWJAP=\"" WIFI_SSID "\",\"" WIFI_PASSWORD "\"\r\n"		//AT指令：连接wifi  
#define ONENET_MQTT_SERVER_INFO "AT+MQTTCONN=0,\"mqtts.heclouds.com\",1883,1\r\n"		//AT指令：连接onenet的mqtt服务器
#define ONENET_MQTT_USERCFG_INFO "AT+MQTTUSERCFG=0,1,\"" ONENET_MQTT_DEVICE_NAME "\",\"" ONENET_MQTT_PRODUCT_ID "\",\"" ONENET_MQTT_TOKEN "\",0,0,\"\"\r\n" 	//AT指令：登入onenet的信息  
#define ONENET_MQTT_PUBTOPIC "AT+MQTTPUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/post\""			//AT指令：设备属性上报请求（发布）
#define ONENET_MQTT_SET_TOPIC "AT+MQTTSUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set\",0\r\n"	//AT指令：设备属性设置请求（订阅）
#define ONENET_MQTT_PUB_SET "AT+MQTTPUB=0,\"$sys/" ONENET_MQTT_PRODUCT_ID "/" ONENET_MQTT_DEVICE_NAME "/thing/property/set_reply\"" 	//AT指令：设备属性设置响应（发布）
/**********************************			无需修改			****************************************/

/*这里放浮点型数据类型的属性*/
const char *floatIdentifiers[] = {"temp","humi","mq2","mq5"};		//在此数组中添加需要上报数据的属性标识符
float floatValues[] = {0,0,0,0};					//对应上方数组对应的数据

/*这里放字符型数据类型的属性*/
const char *stringIdentifiers[] = {""};		
const char *stringValues[] = {""};	            

/*这里放布尔型数据类型的属性*/
const char *boolIdf[] = {"led"};
W_bool boolValue[] = {0}; 

/*这里放整型数据类型的属性*/
const char *intIdf[] = {""};
uint32_t intValue[] = {0}; 


#define wireless_delay_ms  		delay_ms			//这里替换你的延时函数
#define wireless_send_data 		uart2_send_string	//这里替换你的串口发送字符串函数
#define wireless_printf			printf				//这里替换你的打印信息函数

uint8_t W_RxBuffer[RX_BUFFER_SIZE] = {0};
uint16_t W_RxDataCnt = 0;
uint8_t W_RxData = 0;

Wireless_TypeDef WirelessStatus = {0, 0, 0, 0, 0};		//创建无线模块状态结构体


// 创建自己需要云平台控制的属性内容
OneNET_CMD tempThreshold = {"tempThreshold",0};
OneNET_CMD humiThreshold = {"humiThreshold",0};
OneNET_CMD mq2Threshold = {"mq2Threshold",0};
OneNET_CMD mq5Threshold = {"mq5Threshold",0};

//温度阈值
extern float temp_t;
//湿度阈值
extern float humi_t;
//mq2阈值
extern float mq2_t;
//mq5阈值
extern float mq5_t;

/**  
  * @简要  无线模块的串口初始化
  * @参数  无
  * @注意	将自己的无线模块串口初始化代码放在此处
  * @返回值 无  
  */
static void wireless_serial_init(uint32_t Baud)
{
	HAL_UART_Receive_IT(&huart2, (uint8_t *)&W_RxData, 1);
}

/**  
  * @简要  无线模块的串口发送一个字节
  * @参数  uint8_t byte：要发送的字节
  * @注意	无
  * @返回值 无  
  */
static void uart2_send_byte(uint8_t byte)
{
	HAL_UART_Transmit(&huart2, (uint8_t *)&byte, 1, 0xFFFF);
	while(HAL_UART_GetState(&huart2) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
}

/**  
  * @简要  无线模块的串口发送字符串
  * @参数  char *str: 字符数组指针
  * @注意	这里使用了#define 同名替换了wireless_send_data
  * @返回值 无  
  */
static void uart2_send_string(char *str)
{
	while(*str != '\0')
	{
		uart2_send_byte(*str++);
	}
}

/**  
  * @简要  清除无线模块缓存大小
  * @参数  无
  * @注意	无
  * @返回值 无  
  */
static void wireless_clear_buffer(void)
{
	memset(W_RxBuffer, 0, sizeof(W_RxBuffer));
	W_RxDataCnt = 0;
}

/**  
  * @简要  无线模块AT指令发送函数
  * @参数  	char *cmd：要发送的指令字符串
			char *res：期望的返回值字符串结果
			uint8_t sendCount：最大发送次数
			uint8_t printf_enable:选择是否打印本次指令
  * @注意	无
  * @返回值 1：失败
			0：成功
  */
static uint8_t wireless_send_command(char *cmd, char *res, uint8_t sendCount, uint8_t printf_enable)
{
	uint8_t status = 1;
	if(printf_enable == W_ENABLE) wireless_printf("%s",cmd);		//这里可以打印每次发送的指令
	while(sendCount--)
	{
		wireless_send_data(cmd);
		if(WirelessStatus.receiveDataFlag == 1)
		{
			if(strstr((const char *)W_RxBuffer, res) != NULL)		//若找到关键字
			{		
				status = 0;							
				WirelessStatus.receiveDataFlag = 0;
				wireless_clear_buffer();	
				break;
			}
			wireless_clear_buffer();
			WirelessStatus.receiveDataFlag = 0;
		}
		wireless_delay_ms(300);
	}
	if(status == 1 && WirelessStatus.initFlag == 0)    wireless_printf("!!Error!!: %s",cmd);      //初始化完成之前，打印无响应指令
	return status;
}	

/**   
  * @简要  无线模块接收数据提取json格式的数据
  * @参数  	char *cmd：接收到的内容
  * @注意	主要就是把接收的AT指令数据，提取后面‘{’开始的数据
  * @返回值 无  
  */
static char *wireless_receiver_command_to_json(char *cmd)
{
	return strchr((const char*)cmd, '{');		//提取无线模块返回的AT指令中onenet发来的json数据
}

/**   
  * @简要  无线模块接收数据-提取id号
  * @参数  	char *jsonData：json数据
  * @注意	无
  * @返回值 无  
  */
static uint32_t wireless_get_command_id(char *jsonData)
{
	uint32_t cmd_id = 0;		//定义一个id整形变量

    cJSON *json_obj = cJSON_Parse(jsonData);  			// 判断JSON字符串是否合法
    if (json_obj == NULL) 			
	{
        const char *error_ptr = cJSON_GetErrorPtr();	
        if (error_ptr != NULL) 
		{
            wireless_printf("Error before: %s\n", error_ptr);
        }
        return 0xFFFFFFFF;
    }

    cJSON *id_item = cJSON_GetObjectItem(json_obj, "id");  // 提取id字段
    if (cJSON_IsString(id_item) && (id_item->valuestring != NULL)) 
	{
		cmd_id = atoi(id_item->valuestring);		//将id字符串转为整形
    }
    
    cJSON_Delete(json_obj);  // 清理JSON对象

    return cmd_id;
}

/**   
  * @简要  无线模块接收数据后回应
  * @参数  	uint32_t respond_id：回应的id
			uint16_t code：回应的代码，默认：200
			char *msg：回应的字符，默认：success
  * @注意	无
  * @返回值 无  
  */
static void wireless_receive_command_respond(uint32_t respond_id, uint16_t code, char *msg)
{
	char jsonData[256] = {0};
	snprintf(jsonData, sizeof(jsonData),  
					 "%s,\"{\\\"id\\\":\\\"%d\\\"\\,\\\"code\\\":%d\\,\\\"msg\\\":\\\"%s\\\"}\",0,0\r\n",  
					 ONENET_MQTT_PUB_SET, respond_id, code, msg);
	wireless_send_command(jsonData,"OK",1 ,W_ENABLE);
}

/**   
  * @简要  无线模块接收数据-提取命令内容
  * @参数  	char *jsonData：json数据
  * @注意	无
  * @返回值 无  
  */
static void wireless_receiver_command_extract(char *jsonData)
{
	// 检测字符串是否符合json格式
	cJSON *json_obj = cJSON_Parse(jsonData);
    if (json_obj == NULL) {
        const char *error_ptr = cJSON_GetErrorPtr();
        if (error_ptr != NULL) {
            wireless_printf("Error before: %s\r\n", error_ptr);
        }
        return ;
    }
	
	// 提取params字段
    cJSON *params_item = cJSON_GetObjectItem(json_obj, "params");
    if (params_item == NULL) {
        wireless_printf("Params not found\r\n");
        cJSON_Delete(json_obj);
        return;
    }
	
	cJSON *cmd_item = NULL;
	
//	cmd_item  = cJSON_GetObjectItem(params_item, cmd.name);
//	if (cmd_item != NULL && cJSON_IsNumber(cmd_item)) 
//	{
//		cmd.value = (uint16_t)cmd_item->valueint;
//		wireless_printf("cmd.value:%u\r\n",cmd.value);
//	}

//	cmd_item  = cJSON_GetObjectItem(params_item, led.name);
//	if (cmd_item != NULL && cJSON_IsBool(cmd_item)) 
//	{
//		led.value = (uint16_t)cmd_item->valueint;
//		wireless_printf("led.value:%u\r\n",led.value);
//	}	
	
	cmd_item  = cJSON_GetObjectItem(params_item, tempThreshold.name);
	if (cmd_item != NULL && cJSON_IsBool(cmd_item)) 
	{
		tempThreshold.value = (uint16_t)cmd_item->valueint;
		wireless_printf("tempThreshold.value:%u\r\n",tempThreshold.value);
	}	
	
	cmd_item  = cJSON_GetObjectItem(params_item, humiThreshold.name);
	if (cmd_item != NULL && cJSON_IsBool(cmd_item)) 
	{
		humiThreshold.value = (uint16_t)cmd_item->valueint;
		wireless_printf("humiThreshold.value:%u\r\n",humiThreshold.value);
	}	
	
	cmd_item  = cJSON_GetObjectItem(params_item, mq2Threshold.name);
	if (cmd_item != NULL && cJSON_IsBool(cmd_item)) 
	{
		mq2Threshold.value = (uint16_t)cmd_item->valueint;
		wireless_printf("mq2Threshold.value:%u\r\n",mq2Threshold.value);
	}	
	
	cmd_item  = cJSON_GetObjectItem(params_item, mq5Threshold.name);
	if (cmd_item != NULL && cJSON_IsBool(cmd_item)) 
	{
		mq5Threshold.value = (uint16_t)cmd_item->valueint;
		wireless_printf("mq5Threshold.value:%u\r\n",mq5Threshold.value);
	}	
	
	/*这里如果还需要添加其他的控制内容，则如下所示*/
	/*
	//OneNET_CMD door = {"xx",0};  //需要在本.c代码开头创建
	//注意数据类型
	cmd_item  = cJSON_GetObjectItem(params_item, xx.name);
	if (cmd_item != NULL && cJSON_Isxxx(cmd_item)) 
	{
		xx.value = (xx)cmd_item->valueint;
	}
	*/
	cJSON_Delete(json_obj);   //释放内存
}

/**  
  * @简要  无线模块初始化
  * @参数  	无
  * @注意	无
  * @返回值 无  
  */
void wireless_init(void)
{
	const uint8_t sendDataCount = 10;
	wireless_serial_init(1);
	wireless_printf("Start MQTT service\r\n");
	
	if(wireless_send_command("AT+RST\r\n", "", sendDataCount, W_ENABLE) == 1)
		WirelessStatus.initErrorCode |= 1 << 0;
	wireless_delay_ms(2000);		//复位模块，清除连接状态，距离一下条AT指令需要间隔500ms，否则下一条AT指令无效
							
	if(wireless_send_command("ATE0\r\n", "OK", sendDataCount, W_ENABLE) == 1)
		WirelessStatus.initErrorCode |= 1 << 1;
	wireless_delay_ms(2000);		//关闭AT指令回显，减少进入串口中断的“\r\n”判断
	
	if(wireless_send_command("AT+CWAUTOCONN=0\r\n", "OK", sendDataCount, W_ENABLE) == 1)
		WirelessStatus.initErrorCode |= 1 << 2;
	wireless_delay_ms(2000);		//关闭上电自动连接wifi

	if(wireless_send_command("AT+CWMODE=1\r\n", "OK", sendDataCount, W_ENABLE) == 1)
		WirelessStatus.initErrorCode |= 1 << 3;
	wireless_delay_ms(2000);		//启动连接模式一
	
	if(wireless_send_command("AT+CWDHCP=1,0\r\n", "OK", sendDataCount, W_ENABLE) == 1)
		WirelessStatus.initErrorCode |= 1 << 4;
	wireless_delay_ms(2000);		//开启DHCP
	
	if(wireless_send_command(WIRELESS_WIFI_INFO, "OK", 8, W_ENABLE) == 1)
		WirelessStatus.initErrorCode |= 1 << 5;
	wireless_delay_ms(2000);		//连接2.4G wifi

	if(wireless_send_command(ONENET_MQTT_USERCFG_INFO, "OK", sendDataCount, W_ENABLE) == 1)
		WirelessStatus.initErrorCode |= 1 << 6;
	wireless_delay_ms(2000);		//登入onenet服务器
	
	if(wireless_send_command(ONENET_MQTT_SERVER_INFO, "OK", sendDataCount, W_ENABLE) == 1)
		WirelessStatus.initErrorCode |= 1 << 7;
	wireless_delay_ms(2000);		//连接onenet服务器
	
	if(wireless_send_command(ONENET_MQTT_SET_TOPIC, "OK", sendDataCount, W_ENABLE) == 1)
		WirelessStatus.initErrorCode |= 1 << 8;
	wireless_delay_ms(2000);		//订阅属性设置主题

	if(WirelessStatus.initErrorCode == 0) 	//如果初始化没有错误
	{
		WirelessStatus.initFlag = 1;		//初始化完成标志位
		wireless_printf("MQTT service started successfully!!!\r\n");
	}
	else 
	{
		WirelessStatus.initFlag = 0;		//初始化完成标志位
		wireless_printf("MQTT service failed to start,ERROR CODE:0x%04X\r\n", WirelessStatus.initErrorCode);
		wireless_error_handler(WirelessStatus.initErrorCode);  //错误处理函数
	}		
}

/**  
  * @简要  在无线模块初始化函数发送错误时执行
  * @参数  ErrorCode: 错误码，对应的错误状态  
  * @返回值 无  
  */
__weak void wireless_error_handler(uint16_t ErrorCode)
{
	
}

/**  
  * @简要  无线模块接收回调函数
  * @参数  	uint8_t byte：接收的字节
  * @注意	需要将该函数放在对应的串口中断中
  * @返回值 无  
  */
void wireless_receive_callback(uint8_t byte)
{
	W_RxBuffer[W_RxDataCnt++] = byte;   //接收数据转存
	if(W_RxDataCnt > RX_BUFFER_SIZE) 	//判断接收数据是否溢出
	{
		wireless_clear_buffer();
	}				
	
	if( W_RxDataCnt >= 2 && (W_RxBuffer[W_RxDataCnt - 2] == '\r' && W_RxBuffer[W_RxDataCnt - 1] == '\n') ) //判断结束位
	{	
		WirelessStatus.receiveDataFlag = 1;
	}
}


/**   
  * @简要  无线模块数据上报函数
  * @参数  	
			uint8_t intCount：整形个数
			const char *intIdf[]：整形数属性标识符
			uint32_t intValues[]：整形数属性值
			uint8_t floatCount：浮点数属性个数
			const char *floatIdentifiers[]：浮点数属性标识符
			float floatValues[]：浮点数数据
			uint8_t boolCount:布尔属性的个数
			const char *boolIdf[]：布尔属性标识符
			bool boolValue[]：布尔属性值
			uint8_t stringCount：字符型属性个数
			const char *stringIdentifiers[]： 字符型属性标识符
			const char *stringValues[]：字符型数据	
  * @注意	这里的浮点数属性可以向下兼容，例如可以兼容uint8_t,uint16_t ,如果使用整形，需要将"%.2f"改为"%.0f"
  * @返回值 无  
  */
void wireless_publish_data(uint8_t intCount, const char *intIdf[], uint32_t intValues[], uint8_t floatCount, const char *floatIdentifiers[], float floatValues[], uint8_t boolCount, const char *boolIdf[], W_bool boolValue[], uint8_t stringCount, const char *stringIdentifiers[], const char *stringValues[])
{
		// 初始化缓冲区位置  
		size_t bufferPos = 0;  
		const uint8_t BUFFER_SIZE = 255;
		char globalBuffer[BUFFER_SIZE];
		// 拼接主题和JSON开头  
		bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos, "%s,\"{\\\"id\\\":\\\"123\\\"\\,\\\"params\\\":{", ONENET_MQTT_PUBTOPIC);  
		
		// 处理浮点数数据  
		for (unsigned char i = 0; i < intCount; ++i) {  
			bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,  
								  "\\\"%s\\\":{\\\"value\\\":%u\\}%s",  
								  intIdf[i], intValues[i],  
								  (i < intCount - 1 || stringCount > 0 || boolCount > 0 || floatCount > 0) ? "\\," : "");  
		}
	
		// 处理浮点数数据  
		for (unsigned char i = 0; i < floatCount; ++i) {  
			bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,  
								  "\\\"%s\\\":{\\\"value\\\":%.1f\\}%s",  
								  floatIdentifiers[i], floatValues[i],  
								  (i < floatCount - 1 || stringCount > 0 || boolCount > 0) ? "\\," : "");  
		}  
		
		// 处理布尔数数据  
		for (unsigned char i = 0; i < boolCount; ++i) {  
			bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,  
								  "\\\"%s\\\":{\\\"value\\\":%s\\}%s",  
								  boolIdf[i], (boolValue[i] ? "true":"false"),  
								  (i < boolCount - 1 || stringCount > 0) ? "\\," : "");  
		}
		  
		// 处理字符串数据  
		for (unsigned char i = 0; i < stringCount; ++i) {  
			bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos,  
								  "\\\"%s\\\":{\\\"value\\\":\\\"%s\\\"\\}%s",  
								  stringIdentifiers[i], stringValues[i],  
								  (i < stringCount - 1) ? "\\," : "");  
		}  
		// 拼接JSON结尾  
		bufferPos += snprintf(globalBuffer + bufferPos, BUFFER_SIZE - bufferPos, "}}\",0,0\r\n");  
		  
		// 确保我们没有超出缓冲区  
		if (bufferPos >= BUFFER_SIZE) {  
			// 处理错误，例如通过日志记录  
			return;  
		}  
		wireless_send_command(globalBuffer,"OK",1, W_ENABLE);
}

/**   
  * @简要  获取无线模块数据接收标志位
  * @参数  	无
  * @注意	无
  * @返回值 数据接收标志位  
  */
uint8_t wireless_get_receive_flag(void)
{
	uint8_t flag = WirelessStatus.receiveDataFlag;
	WirelessStatus.receiveDataFlag = 0;
	return flag;
}

/**   
  * @简要  获取无线模块命令接收标志位
  * @参数  	无
  * @注意	无
  * @返回值 命令接收标志位  
  */
uint8_t wireless_get_command_flag(void)
{
	uint8_t flag = WirelessStatus.receiveCMDFlag;
	WirelessStatus.receiveCMDFlag = 0;
	return flag;
}

/**   
  * @简要  无线模块接收数据-进行各类操作
  * @参数  	无
  * @注意	无
  * @返回值 无  
  */
void wireless_receiver_command(void)
{
	if(strstr((char *)W_RxBuffer, "+MQTTSUBRECV") != NULL)	//如果判断开头为："+MQTTSUBRECV"
	{
		char *jsonData = NULL;
		uint32_t cmd_id;
		jsonData = wireless_receiver_command_to_json((char *)W_RxBuffer);	//提取json数据
		wireless_printf("jsonData:\r\n%s\r\n",jsonData);		//显示接收到的json数据
		cmd_id = wireless_get_command_id(jsonData);					//获得命令的id号
		wireless_receiver_command_extract(jsonData);				//提取指令
		wireless_receive_command_respond(cmd_id, 200, "success");	//回应onenet
		WirelessStatus.receiveCMDFlag = 1;
	}
	wireless_clear_buffer();		//清除无线模块接收数据缓存
}

/**  
  * @简要  无线模块命令控制
  * @参数  	无
  * @注意	这里可以自由封装代码
  * @返回值 无  
  */
void wireless_command_control(void)
{
//	if(led.value == 1){
//		HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);
//		boolValue[0] = 1;
//	} //控制灯的状态后，修改对应的属性值，再发送到onenet
//	else if(led.value == 0){
//		HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);
//		boolValue[0] = 0;
//	}
	temp_t = (float)tempThreshold.value;
	humi_t = (float)humiThreshold.value;
	mq2_t = (float)mq2Threshold.value;
	mq5_t = (float)mq5Threshold.value;
}
